import EventSource, { EventSourceListener } from "react-native-sse"

export interface SSEConfig {
  url: string
  headers?: Record<string, string>
  method?: "GET" | "POST" | "PUT" | "DELETE"
  body?: string | object
  timeout?: number
  timeoutBeforeConnection?: number
  pollingInterval?: number
  debug?: boolean
}

export interface SSECallbacks<T = any> {
  onOpen?: () => void
  onMessage?: (data: T) => void
  onError?: (error: any) => void
  onComplete?: () => void
  onClose?: () => void
}

export interface SSEClient {
  connect: () => void
  disconnect: () => void
  getConnectionStatus: () => boolean
}

export interface SSEClientOptions {
  config: SSEConfig
  callbacks: SSECallbacks
}

const normaliseBody = (body?: SSEConfig["body"]): string | undefined => {
  if (typeof body === "string" || body === undefined) return body
  try {
    return JSON.stringify(body)
  } catch (error) {
    throw new Error(`Failed to serialise SSE request body: ${error}`)
  }
}

const createEventListener = (
  callbacks: SSECallbacks,
  updateConnectionStatus: (status: boolean) => void,
): EventSourceListener => {
  return (event) => {
    const eventType = event.type as string

    switch (eventType) {
      case "open":
        callbacks.onOpen?.()
        updateConnectionStatus(true)
        break

      case "message": {
        try {
          const data: any = event.data
          callbacks.onMessage?.(data)
        } catch (error) {
          callbacks.onError?.(error instanceof Error ? error : new Error(String(error)))
        }
        break
      }

      case "error":
        callbacks.onError?.(new Error(event.message || "SSE connection error"))
        break

      case "timeout":
      case "exception":
      case "close":
        updateConnectionStatus(false)
        callbacks.onClose?.()
        break

      case "done":
        updateConnectionStatus(false)
        callbacks.onComplete?.()
        break

      default:
        break
    }
  }
}

export const createSSEClient = (options: SSEClientOptions): SSEClient => {
  let eventSource: EventSource | null = null
  let isConnected = false

  const updateConnectionStatus = (status: boolean) => {
    isConnected = status
  }

  const connect = () => {
    if (isConnected || eventSource) {
      console.warn("SSE connection already established")
      return
    }

    try {
      eventSource = new EventSource(options.config.url, {
        headers: {
          "Accept": "text/event-stream",
          "Cache-Control": "no-cache",
          ...options.config.headers,
        },
        method: options.config.method ?? "GET",
        body: normaliseBody(options.config.body),
        timeout: options.config.timeout ?? 0,
        timeoutBeforeConnection: options.config.timeoutBeforeConnection ?? 500,
        pollingInterval: options.config.pollingInterval ?? 0,
        debug: options.config.debug ?? false,
      })
      const listener = createEventListener(options.callbacks, updateConnectionStatus)

      eventSource.addEventListener("open", listener)
      eventSource.addEventListener("message", listener)
      eventSource.addEventListener("error", listener)
      ;(["close", "done", "timeout", "exception"] as const).forEach((eventName) => {
        ;(eventSource as any).addEventListener(eventName, listener)
      })

      updateConnectionStatus(true)
    } catch (error) {
      options.callbacks.onError?.(error as Error)
      updateConnectionStatus(false)
    }
  }

  const disconnect = () => {
    eventSource?.close()
    eventSource = null
    updateConnectionStatus(false)
  }

  const getConnectionStatus = () => isConnected

  return {
    connect,
    disconnect,
    getConnectionStatus,
  }
}

export interface OpenAISSEClient {
  connect: () => void
  disconnect: () => void
  getConnectionStatus: () => boolean
  getCompleteMessage: () => string
  isComplete: () => boolean
  reset: () => void
}

interface OpenAICallbacks extends SSECallbacks<string> {}

export const createOpenAISSEClient = (
  config: Omit<SSEConfig, "url">,
  callbacks: OpenAICallbacks,
): OpenAISSEClient => {
  let messageBuffer = ""
  let streamComplete = false

  const handleMessage = (data: any) => {
    if (data === "[DONE]") {
      streamComplete = true
      callbacks.onComplete?.()
      return
    }

    try {
      const parsed = typeof data === "string" ? JSON.parse(data) : data
      const delta = parsed?.choices?.[0]?.delta?.content
      if (delta) {
        messageBuffer += String(delta)
        callbacks.onMessage?.(String(delta))
      }

      if (parsed?.choices?.[0]?.finish_reason) {
        streamComplete = true
        callbacks.onComplete?.()
      }
    } catch (error) {
      callbacks.onError?.(error instanceof Error ? error : new Error(String(error)))
    }
  }

  const sseClient = createSSEClient({
    config: {
      url: "https://api.openai-hk.com/v1/chat/completions",
      ...config,
    },
    callbacks: {
      ...callbacks,
      onMessage: handleMessage,
    },
  })

  const reset = () => {
    messageBuffer = ""
    streamComplete = false
  }

  const getCompleteMessage = () => messageBuffer
  const isComplete = () => streamComplete

  return {
    connect: sseClient.connect,
    disconnect: sseClient.disconnect,
    getConnectionStatus: sseClient.getConnectionStatus,
    getCompleteMessage,
    isComplete,
    reset,
  }
}
